﻿using BGYMqttServer.Models;
using Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MQTTnet;
using MQTTnet.Client.Receiving;
using MQTTnet.Server;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace BGYMqttServer.Handlers
{
    /// <summary>
    /// mqtt消息服务
    /// </summary>
    public class MqttMessageHandler : ISingletonDependency, IMqttApplicationMessageReceivedHandler
    {
        private readonly IServiceProvider services;
        private readonly ILogger<MqttMessageHandler> logger;

        /// <summary>
        /// 服务器的客户端id
        /// </summary>
        public static string ClientId = "bgy";

        /// <summary>
        /// 任务表
        /// </summary>
        private readonly ConcurrentDictionary<int, ManualTask> taskTable = new ConcurrentDictionary<int, ManualTask>();

        /// <summary>
        /// mqtt消息服务
        /// </summary>
        /// <param name="services"></param>
        /// <param name="logger"></param>
        public MqttMessageHandler(IServiceProvider services, ILogger<MqttMessageHandler> logger)
        {
            this.services = services;
            this.logger = logger;
        }

        /// <summary>
        /// 发送请求
        /// </summary>
        /// <typeparam name="TRequestBody"></typeparam>
        /// <typeparam name="TResponseBody"></typeparam>
        /// <param name="coId">小区id</param>
        /// <param name="request">请求</param> 
        /// <param name="timeout">超时时间</param> 
        /// <returns></returns>
        public async Task<MqttMessage<TResponseBody>> SendAsync<TRequestBody, TResponseBody>(string coId, MqttMessage<TRequestBody> request, TimeSpan timeout)
        {
            try
            {
                var task = new ManualTask<MqttMessage<TResponseBody>>();
                task.SetExceptionAfter(new TimeoutException(), timeout);
                this.taskTable.TryAdd(request.Id, task);

                await this.PostAsync(coId, request);
                return await task;
            }
            finally
            {
                this.taskTable.TryRemove(request.Id, out _);
            }
        }


        /// <summary>
        /// 发送请求
        /// </summary>
        /// <typeparam name="TBody"></typeparam>
        /// <param name="eqId">门口机id</param>
        /// <param name="request">请求</param> 
        /// <returns></returns>
        private Task PostAsync<TBody>(string eqId, MqttMessage<TBody> request)
        {
            var topic = $"devices/{eqId}";
            var payload = JsonTextSerializer.SerializeToUtf8Bytes(request);
            var message = new MqttApplicationMessage
            {
                Topic = topic,
                Payload = payload,
            };
            return this.services.GetService<IMqttServer>().PublishAsync(message);
        }


        /// <summary>
        /// 处理收到的消息
        /// </summary>
        /// <param name="eventArgs"></param>
        /// <returns></returns>
        async Task IMqttApplicationMessageReceivedHandler.HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
        {
            var clientId = eventArgs.ClientId;
            if (clientId == default || clientId == ClientId)
            {
                return;
            }

            if (eventArgs.ApplicationMessage.Topic.Contains(ClientId) == false)
            {
                return;
            }

            try
            {
                var payload = eventArgs.ApplicationMessage.Payload;
                var message = JsonTextSerializer.Deserialize<MqttMessage>(payload);

                if (this.taskTable.TryRemove(message.Id, out var task))
                {
                    var value = JsonTextSerializer.Deserialize(payload, task.ResultType);
                    task.SetResult(value);
                }
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, ex.Message);
            }
            await Task.CompletedTask;
        }
    }
}